www.gusucode.com > 基于Visual C++高级界面特效制作百例源码程序 > 基于Visual C++高级界面特效制作百例源码程序/code/char21/vector_control/VectorCtl.cpp
#include "stdafx.h" #include "VectorCtl.h" #include <math.h> CVectorCtl::CVectorCtl () : m_bBmpCreated (FALSE), m_bImageChange (TRUE), m_bBackgroundBitmapUsed (FALSE), m_clrDiffuse (DEFAULT_DIFFUSE), m_clrAmbient (DEFAULT_AMBIENT), m_clrLight (DEFAULT_LIGHT), m_clrBackgroundStart (DEFAULT_START_BACKGROUND_COLOR), m_clrBackgroundEnd (DEFAULT_END_BACKGROUND_COLOR), m_dSpecularExponent (DEFAULT_SPEC_EXP), m_bHasFocus (FALSE), m_bSelected (FALSE), m_bFrontVector (FALSE), m_iLastMouseY (VAL_NOT_IN_USE), m_iLastMouseX (VAL_NOT_IN_USE), m_dSensitivity (20.0), m_procVectorChanging (NULL), m_procVectorChanged (NULL) { double DefaultVec[3] = DEFAULT_VEC; for (int i=0; i<3; i++) { m_dVec[i] = DefaultVec[i]; pCtl[i] = NULL; } } CVectorCtl::~CVectorCtl () { if (m_bBmpCreated) m_dcMem.SelectObject (m_pOldBitmap); ClearBackgroundBitmap (); } // Owner-drawn control service function: void CVectorCtl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) { CDC *pDC = CDC::FromHandle (lpDrawItemStruct->hDC); // Get CDC to draw if (!m_bSelected && lpDrawItemStruct->itemState & ODS_SELECTED) { // Just got re-selected (user starts a new mouse dragging session) m_iLastMouseX = VAL_NOT_IN_USE; m_iLastMouseY = VAL_NOT_IN_USE; } else if (m_bSelected && // Last state was selected !(lpDrawItemStruct->itemState & ODS_SELECTED) && // New state is NOT selected (lpDrawItemStruct->itemState & ODS_FOCUS) && // New state is still in focus m_procVectorChanged) // User asked for a callback // User has left the track-ball and asked for a callback. m_procVectorChanged (m_dVec[0], m_dVec[1], m_dVec[2]); // Call it! m_bHasFocus = lpDrawItemStruct->itemState & ODS_FOCUS; // Update focus status m_bSelected = lpDrawItemStruct->itemState & ODS_SELECTED; // Update selection status if (!m_bBmpCreated) // 1st time InitBitmap (lpDrawItemStruct, pDC); if (m_bImageChange) { // Image has changes - recalc it! if (m_procVectorChanging) // User has specified a callback m_procVectorChanging (m_dVec[0], m_dVec[1], m_dVec[2]); // Call it! BuildImage (lpDrawItemStruct); m_bImageChange = FALSE; } pDC->BitBlt (0,0,m_iWidth, m_iHeight, &m_dcMem, 0, 0, SRCCOPY); // Update screen } // Mouse was dragged void CVectorCtl::OnMouseDrag (int ixMove, int iyMove) { RotateByXandY (double(-iyMove) / m_dSensitivity, double(ixMove) / m_dSensitivity); } // Recalc ball image void CVectorCtl::BuildImage (LPDRAWITEMSTRUCT lpDrawItemStruct) { int xf, yf; for (int x=0; x<m_iWidth; x++) // Scan all columns for (int y=0; y<m_iHeight; y++) { // Scan all rows xf = x-m_iXCenter; // Find distance from center yf = y-m_iYCenter; if (xf*xf + yf*yf <= m_iSqrRadius) { // Point on ball surface double vx = double(xf) / double(m_iRadius), vy = double(yf) / double(m_iRadius), vz = sqrt (1.0 - vx*vx - vy*vy); // Find ball's normal m_dcMem.SetPixelV (x,y, CalcLight (vx,vy,vz)); } } } // Normalize a vector to unit size BOOL CVectorCtl::Normalize () { double Norm = m_dVec[0] * m_dVec[0] + m_dVec[1] * m_dVec[1] + m_dVec[2] * m_dVec[2]; if (Norm > EPS) { Norm = sqrt (Norm); m_dVec[0] /= Norm; m_dVec[1] /= Norm; m_dVec[2] /= Norm; return TRUE; } else { // Reset to defualt vector double DefaultVec[3] = DEFAULT_VEC; for (int i=0; i<3; i++) m_dVec[i] = DefaultVec[i]; return FALSE; } } // Calculate lightning effect for specific pixel on ball's surface COLORREF CVectorCtl::CalcLight (double dx, double dy, double dz) { double NL = dx * m_dVec[0] + dy * m_dVec[1] + dz * m_dVec[2], RV = 2.0 * NL, rx = m_dVec[0] - (dx * RV), ry = m_dVec[1] - (dy * RV), rz = m_dVec[2] - (dz * RV); if (NL < 0.0) // Diffuse coefficient NL = 0.0; RV = max (0.0, -rz); RV = double(pow (RV, m_dSpecularExponent)); int r = int ( double(GetRValue(m_clrDiffuse)) * NL + // Diffuse double(GetRValue(m_clrLight)) * RV + // Specular double(GetRValue(m_clrAmbient))), // Ambient g = int ( double(GetGValue(m_clrDiffuse)) * NL + // Diffuse double(GetGValue(m_clrLight)) * RV + // Specular double(GetGValue(m_clrAmbient))), // Ambient b = int ( double(GetBValue(m_clrDiffuse)) * NL + // Diffuse double(GetBValue(m_clrLight)) * RV + // Specular double(GetBValue(m_clrAmbient))); // Ambient r = min (255, r); // Cutoff highlight g = min (255, g); b = min (255, b); return RGB(BYTE(r),BYTE(g),BYTE(b)); } // Start memory buffer bitmap and measure it void CVectorCtl::InitBitmap (LPDRAWITEMSTRUCT lpDrawItemStruct, CDC *pDC) { m_iWidth = lpDrawItemStruct->rcItem.right - lpDrawItemStruct->rcItem.left; m_iHeight = lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top; m_bmpBuffer.CreateCompatibleBitmap (pDC, m_iWidth, m_iHeight); m_bBmpCreated = TRUE; m_dcMem.CreateCompatibleDC (pDC); m_pOldBitmap = m_dcMem.SelectObject (&m_bmpBuffer); SetRadius (max (min (m_iWidth, m_iHeight) - 2, 0) / 2); SetCenter (m_iWidth / 2, m_iHeight / 2); CreateBackground (); } // Set new specular intensity BOOL CVectorCtl::SetSpecularExponent (double dExp) { if (dExp < 1.0 || dExp > 200.0) return FALSE; m_dSpecularExponent = dExp; Redraw (); return TRUE; } // Rotate our vector around the X and Y axis void CVectorCtl::RotateByXandY (double XRot, double YRot) { // Angles are in radians if (XRot == 0.0 && YRot == 0.0) return; double cx = cos(XRot), sx = sin(XRot), cy = cos(YRot), sy = sin(YRot), dx = m_dVec[0] * cy + m_dVec[1] * sx * sy + m_dVec[2] * cx * sy, dy = m_dVec[1] * cx - m_dVec[2] * sx, dz = -m_dVec[0] * sy + m_dVec[1] * sx * cy + m_dVec[2] * cx * cy; if (!m_bFrontVector || dz >= 0.0) { // Vector is bounds free m_dVec[0] = dx; m_dVec[1] = dy; m_dVec[2] = dz; } else { // Otherwise, do not allow Z to be negative (light shines from behind) m_dVec[2] = 0.0; m_dVec[0] = dx; m_dVec[1] = dy; Normalize (); } Redraw (); } void CVectorCtl::UpdateAxisControls () { CString cs; for (int i=0; i<3; i++) if (pCtl[i]) { cs.Format ("%+1.5f",m_dVec[i]); pCtl[i]->SetWindowText (cs); } } void CVectorCtl::SetAxisControl (int nXCtl, int nYCtl, int nZCtl) { pCtl[0] = GetParent()->GetDlgItem(nXCtl); pCtl[1] = GetParent()->GetDlgItem(nYCtl); pCtl[2] = GetParent()->GetDlgItem(nZCtl); } void CVectorCtl::SetRadius (UINT uRadius) { m_iRadius = uRadius; m_iSqrRadius = m_iRadius * m_iRadius; CreateBackground (); Redraw (TRUE); } void CVectorCtl::SetCenter (UINT uHorizPos, UINT uVertPos) { m_iXCenter = uHorizPos; m_iYCenter = uVertPos; CreateBackground (); Redraw (TRUE); } void CVectorCtl::SetAxis (double d, int nAxis) { if (fabs(d)>=1.0) { m_dVec[nAxis]=d > 1.0 ? 1.0 : -1.0; m_dVec[(nAxis+1) %3]=m_dVec[(nAxis+2) %3]=0.0; Redraw (); return; } m_dVec[nAxis] = d; Normalize (); Redraw (); } void CVectorCtl::SetVector (double dx, double dy, double dz) { m_dVec[0] = dx; m_dVec[1] = dy; m_dVec[2] = dz; Normalize (); Redraw (); } void CVectorCtl::SetBackgroundColor (COLORREF clrStart, COLORREF clrEnd) { ClearBackgroundBitmap (); m_clrBackgroundStart = clrStart; m_clrBackgroundEnd = clrEnd; CreateBackground (); } BOOL CVectorCtl::SetBackgroundImage (UINT uBackgroundBitmapID) { if (m_bBackgroundBitmapUsed) { ClearBackgroundBitmap (); CreateBackground (); } if (!m_bmpBack.LoadBitmap (uBackgroundBitmapID)) return FALSE; m_bBackgroundBitmapUsed = TRUE; CreateBackground (); return TRUE; } void CVectorCtl::CreateBackground () { if (!m_bBmpCreated) return; // No image yet if (!m_bBackgroundBitmapUsed) { // No background used - fill with gradient color double r = GetRValue (m_clrBackgroundStart), g = GetGValue (m_clrBackgroundStart), b = GetBValue (m_clrBackgroundStart), rd = double (GetRValue (m_clrBackgroundEnd) - r) / double (m_iHeight), gd = double (GetGValue (m_clrBackgroundEnd) - g) / double (m_iHeight), bd = double (GetBValue (m_clrBackgroundEnd) - b) / double (m_iHeight); for (int j=0; j<m_iHeight; j++) { for (int i=0; i<m_iWidth; i++) m_dcMem.SetPixelV (i,j, RGB (BYTE(r),BYTE(g),BYTE(b))); r+=rd; g+=gd; b+=bd; } Redraw (TRUE); return; } // Bitmap used : tile it in back CDC DCtmp; BITMAP tmpBitmap; m_bmpBack.GetBitmap (&tmpBitmap); int iTmpWidth = tmpBitmap.bmWidth, iTmpHeight = tmpBitmap.bmHeight; DCtmp.CreateCompatibleDC (&m_dcMem); m_pOldBitmap = DCtmp.SelectObject (&m_bmpBack); for (int i=0; i<m_iWidth; i++) for (int j=0; j<m_iHeight; j++) m_dcMem.SetPixelV (i,j, DCtmp.GetPixel (i % iTmpWidth, j % iTmpHeight)); DCtmp.SelectObject (m_pOldBitmap); Redraw (TRUE); } void CVectorCtl::ClearBackgroundBitmap () { if (!m_bBackgroundBitmapUsed) return; m_bmpBack.DeleteObject (); m_bBackgroundBitmapUsed = FALSE; } BOOL CVectorCtl::SetSensitivity (UINT uSens) { if (uSens == 0) return FALSE; m_dSensitivity = double(uSens); return TRUE; } void CVectorCtl::Redraw (BOOL bErase) { m_bImageChange = TRUE; UpdateAxisControls(); Invalidate (bErase); } LRESULT CVectorCtl::WindowProc( UINT message, WPARAM wParam, LPARAM lParam ) { if (message == WM_MOUSEMOVE && (wParam & MK_LBUTTON)) { // Mouse has moved and left button is down WORD wCurX = LOWORD(lParam); WORD wCurY = HIWORD(lParam); int iCurX = wCurX > 3000 ? int(int(wCurX) - 0xffff) : int(wCurX); int iCurY = wCurY > 3000 ? int(int(wCurY) - 0xffff) : int(wCurY); OnMouseDrag (m_iLastMouseX != VAL_NOT_IN_USE ? iCurX - m_iLastMouseX : 0, m_iLastMouseY != VAL_NOT_IN_USE ? iCurY - m_iLastMouseY : 0); m_iLastMouseX = iCurX; m_iLastMouseY = iCurY; } return CButton::WindowProc (message, wParam, lParam); }